/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.sequence.ui.composites;

import java.util.Iterator;

import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider;
import org.eclipse.gef.EditPart;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Expression;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.InstanceValue;
import org.eclipse.uml2.uml.Lifeline;
import org.eclipse.uml2.uml.LiteralBoolean;
import org.eclipse.uml2.uml.LiteralInteger;
import org.eclipse.uml2.uml.LiteralNull;
import org.eclipse.uml2.uml.LiteralString;
import org.eclipse.uml2.uml.LiteralUnlimitedNatural;
import org.eclipse.uml2.uml.UMLFactory;
import org.eclipse.uml2.uml.ValueSpecification;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagram.sequence.ui.dialogs.LifelineSelectorInstanceValueTreeSelectionDialog;
import com.cea.papyrus.diagram.sequence.ui.validators.BooleanInputValidator;
import com.cea.papyrus.diagram.sequence.ui.validators.IntegerInputValidator;
import com.cea.papyrus.diagram.sequence.ui.validators.UMLLabelProvider;
import com.cea.papyrus.diagram.sequence.ui.validators.UnlimitedNaturalInputValidator;
import com.cea.papyrus.tabbedproperties.composites.DecoratedLabelComposite;


public class LifelineSelectorComposite extends DecoratedLabelComposite {

	/**
	 * debug channel.
	 */
	protected static int channel = 501;

	/**
	 * behavior selected
	 */
	protected ValueSpecification selector = null;
	
	protected ITreeContentProvider contentProvider;
	protected LabelProvider labelProvider;
	
	/**
	 * Default Constructor.
	 */
	public LifelineSelectorComposite(){
		super("Selector");
	}

	/**
	 * Constructor with given name for label.
	 */
	public LifelineSelectorComposite(String name){
		super(name);
	}

	/**
	 * The Menu to show all kinds of possible Behavior.
	 */
	private Menu menu;

	/**
	 * Various menu items.
	 */
	private MenuItem 
	expressionItem,
	instanceValueItem,
	literalBooleanItem,
	literalIntegerItem,
	literalStringItem,
	literalUnlimitedNaturalItem,
	literalNullItem;

	/**
	 * Associated selection listeners.
	 */
	private SelectionListener
	expressionItemListener,
	instanceValueItemListener,
	literalBooleanItemListener,
	literalIntegerItemListener,
	literalStringItemListener,
	literalUnlimitedNaturalItemListener,
	literalNullItemListener;

	/**
	 * Property page
	 */
	private TabbedPropertySheetPage tabbedPropertySheetPage = null;
	
	/**
	 * Loads a new property page on the effect
	 */
	@Override
	public void editText(){
		//TODO: find a solution to load a new property page
		if(selector != null){
			EditPart selection = null;
			
			Iterator<EditPart> i = LookForElement.getAllOutlineEditParts().iterator();
			while((selection == null) && i.hasNext()){
				EditPart current = i.next() ;
				if((current.getModel() != null) && current.getModel().equals(selector))
					selection = current ;
			}
			if(selection != null){
				tabbedPropertySheetPage.selectionChanged(getActiveEditor(), new StructuredSelection(selection));
			}
		}
	}

	/**
	 * Stores the tabbed property page before handling the call
	 * to createContent
	 */
	public Composite createContent(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage){
		this.tabbedPropertySheetPage = tabbedPropertySheetPage;
		return createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
	}

	/**
	 * Creates contents.
	 * 
	 * @param parent Composite.
	 * @param factory TabbedPropertySheetWidgetFactory.
	 * 
	 * @return specific Composite.
	 */
	@Override
	public Composite createContent(Composite parent, TabbedPropertySheetWidgetFactory factory){
		Composite composite = super.createContent(parent, factory);

		//add an additional menu to the add Button
		createMenuForAddButton();

		label.setText("Selector:");
		label.setToolTipText("Selector");
		
		return composite;
	}

	/**
	 * Adds an additional menu to the addButton. So user can choose which type of trigger to create.
	 */
	private void createMenuForAddButton(){
		menu = new Menu (addButton);
		
		// add Expression menu item
		expressionItem = new MenuItem (menu, SWT.PUSH);
		expressionItem.setText("Expression");
		expressionItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(Expression.class, null));
		expressionItem.addSelectionListener(
				expressionItemListener = 
					new AddElementSelectionListener(Expression.class));

		// add InstanceValue menu item
		instanceValueItem = new MenuItem (menu, SWT.PUSH);
		instanceValueItem.setText("InstanceValue");
		instanceValueItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(InstanceValue.class, null));
		instanceValueItem.addSelectionListener(
				instanceValueItemListener = 
					new AddElementSelectionListener(InstanceValue.class));
		
		// add LiteralBoolean menu item
		literalBooleanItem = new MenuItem (menu, SWT.PUSH);
		literalBooleanItem.setText("LiteralBoolean");
		literalBooleanItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralBoolean.class, null));
		literalBooleanItem.addSelectionListener(
				literalBooleanItemListener = 
					new AddElementSelectionListener(LiteralBoolean.class));
		
		// add LiteralInteger menu item
		literalIntegerItem = new MenuItem (menu, SWT.PUSH);
		literalIntegerItem.setText("LiteralInteger");
		literalIntegerItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralInteger.class, null));
		literalIntegerItem.addSelectionListener(
				literalIntegerItemListener = 
					new AddElementSelectionListener(LiteralInteger.class));
		
		// add LiteralString menu item
		literalStringItem = new MenuItem (menu, SWT.PUSH);
		literalStringItem.setText("LiteralString");
		literalStringItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralString.class, null));
		literalStringItem.addSelectionListener(
				literalStringItemListener = 
					new AddElementSelectionListener(LiteralString.class));
		
		// add LiteralUnlimitedNatural menu item
		literalUnlimitedNaturalItem = new MenuItem (menu, SWT.PUSH);
		literalUnlimitedNaturalItem.setText("LiteralUnlimitedNatural");
		literalUnlimitedNaturalItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralUnlimitedNatural.class, null));
		literalUnlimitedNaturalItem.addSelectionListener(
				literalUnlimitedNaturalItemListener = 
					new AddElementSelectionListener(LiteralUnlimitedNatural.class));
		
		// add LiteralNull menu item
		literalNullItem = new MenuItem (menu, SWT.PUSH);
		literalNullItem.setText("LiteralNull");
		literalNullItem.setImage(PapyrusPlugin.UML_LABEL_PROVIDER.getImage(LiteralNull.class, null));
		literalNullItem.addSelectionListener(
				literalNullItemListener = 
					new AddElementSelectionListener(LiteralNull.class));

		addButton.setMenu (menu);
	}

	/**
	 * Refreshes the composite.
	 */
	@Override
	public void refresh() {
		Element elt = element.getUml2Element();
		
		assert elt != null : 
			"Passed argument is null";
		assert elt instanceof Lifeline : 
			"Passed argument is not a Lifeline";
		
		selector = ((Lifeline) elt).getSelector();
		if(selector != null){
			if(!text.isDisposed()) {
				text.setImage(labelProvider.getImage(selector));
				text.setText(labelProvider.getText(selector));
			}
			if(!addButton.isDisposed())
				addButton.setEnabled(false);
			if(!removeButton.isDisposed())
				removeButton.setEnabled(true);
		}
		else{
			if(!text.isDisposed()){
				text.setText("Selector Unset");
				text.setImage(null);
			}
			if(!addButton.isDisposed())
				addButton.setEnabled(true);
			if(!removeButton.isDisposed())
				removeButton.setEnabled(false);
		}
	}

	/**
	 * Executed behavior when addButton pressed.
	 */
	public void addButtonPressed(){
		if(!menu.isDisposed())
			menu.setVisible(true);
	}

	/**
	 * Executed behavior when removeButton pressed.
	 */
	public void removeButtonPressed(){
		Element elt = element.getUml2Element();
		
		assert elt != null : 
			"Passed argument is null";
		assert elt instanceof Lifeline : 
			"Passed argument is not a Lifeline";

		if(!text.isDisposed())
			text.setText("Selector Unset");

		selector = ((Lifeline) element.getUml2Element()).getSelector();
		if(selector != null) {
			((Lifeline) element.getUml2Element()).setSelector(null);
		}

		//refresh contents
		refresh();
	}

	/**
	 * Disposes of the composite
	 */
	public void dispose(){
		super.dispose();
		
		if ((expressionItem != null) && !expressionItem.isDisposed())
			expressionItem.removeSelectionListener(expressionItemListener);
		if ((instanceValueItem != null) && !instanceValueItem.isDisposed())
			instanceValueItem.removeSelectionListener(instanceValueItemListener);
		if ((literalBooleanItem != null) && !literalBooleanItem.isDisposed())
			literalBooleanItem.removeSelectionListener(literalBooleanItemListener);
		if ((literalIntegerItem != null) && !literalIntegerItem.isDisposed())
			literalIntegerItem.removeSelectionListener(literalIntegerItemListener);
		if ((literalStringItem != null) && !literalStringItem.isDisposed())
			literalStringItem.removeSelectionListener(literalStringItemListener);
		if ((literalUnlimitedNaturalItem != null) && !literalUnlimitedNaturalItem.isDisposed())
			literalUnlimitedNaturalItem.removeSelectionListener(literalUnlimitedNaturalItemListener);
		if ((literalNullItem != null) && !literalNullItem.isDisposed())
			literalNullItem.removeSelectionListener(literalNullItemListener);

	}

	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener {
		
		/**
		 * Constructor with given class.
		 * 
		 * @param elementClass Class
		 */
		public AddElementSelectionListener(Class<?> elementClass) {
			this.elementClass = elementClass;
		}

		/**
		 * Stored Class.
		 */
		public Class<?> elementClass;

		/**
		 * Does nothing.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/**
		 * What to do when item selected depending on the type of Behavior we want to associate a BehavioredClassifier to.
		 * 
		 * @param e SelectionEvent.
		 */
		public void widgetSelected(SelectionEvent e){
			Element elt = element.getUml2Element();
			
			assert elt != null : 
				"Passed argument is null";
			assert elt instanceof Lifeline : 
				"Passed argument is not a Lifeline";

			int result=42;
			
			Dialog dialog = null;
			Lifeline lifeline = ((Lifeline) elt);
				
			// propose existing behaviors
			if(elementClass.equals(InstanceValue.class)){
				dialog = 
					new LifelineSelectorInstanceValueTreeSelectionDialog(
							composite.getShell(), 
							labelProvider,
							contentProvider,
							lifeline);
				((LifelineSelectorInstanceValueTreeSelectionDialog) 
						dialog).setInput(element.getUml2Element().getModel());				
				result = dialog.open();
				
			} else if (elementClass.equals(Expression.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"Expression", 
							"Enter expression symbol (String) :", 
							null, null);
				result = dialog.open();
				
			} else if (elementClass.equals(LiteralNull.class)) {
				// Nothing to ask
				result = IDialogConstants.OK_ID;
			
			} else if (elementClass.equals(LiteralString.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralString", 
							"Enter string value :", 
							null, null);
				result = dialog.open();
			
			} else if (elementClass.equals(LiteralInteger.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralInteger", 
							"Enter integer value :", 
							"1", 
							new IntegerInputValidator());
				result = dialog.open();
				
			} else if (elementClass.equals(LiteralUnlimitedNatural.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralUnlimitedNatural", 
							"Enter unlimited natural value (0, 1, 2, ..., *) :", 
							null, 
							new UnlimitedNaturalInputValidator());
				result = dialog.open();
			
			} else if (elementClass.equals(LiteralBoolean.class)) {
				dialog = 
					new InputDialog(
							composite.getShell(), 
							"LiteralBoolean", 
							"Enter boolean value (true | false) :", 
							"true", 
							new BooleanInputValidator());
				result = dialog.open();
			}
			
			// result treatment
			if(result==IDialogConstants.OK_ID){
				ValueSpecification selector = null;
				if (elementClass.equals(InstanceValue.class)) {
					InstanceSpecification instance = 
						(InstanceSpecification)
						((LifelineSelectorInstanceValueTreeSelectionDialog) dialog).getFirstResult();
					
					// Create InstanceValue
					selector = UMLFactory.eINSTANCE.createInstanceValue();
					selector.setName(ModelUtil.createUniqueName(lifeline.getNamespace(), "InstanceValue_"));
					((InstanceValue) selector).setInstance(instance);		
				
				} else if (elementClass.equals(Expression.class)) {
					String symbol = ((InputDialog) dialog).getValue();
					
					// Create Expression
					selector = UMLFactory.eINSTANCE.createExpression();
					((Expression) selector).setSymbol(symbol);
					selector.setName(ModelUtil.createUniqueName(lifeline.getNamespace(), "Expression_"));
					
				} else if (elementClass.equals(LiteralNull.class)) {
										
					// Create LiteralNull
					selector = UMLFactory.eINSTANCE.createLiteralNull();
					selector.setName(ModelUtil.createUniqueName(lifeline.getNamespace(), "LiteralNull_"));
				
				} else if (elementClass.equals(LiteralString.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					
					// Create LiteralString
					selector = UMLFactory.eINSTANCE.createLiteralString();
					((LiteralString) selector).setValue(inputString);
					selector.setName(ModelUtil.createUniqueName(lifeline.getNamespace(), "LiteralString_"));
					
				} else if (elementClass.equals(LiteralInteger.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					Integer inputInteger = Integer.parseInt(inputString);
					
					// Create LiteralInteger
					selector = UMLFactory.eINSTANCE.createLiteralInteger();
					((LiteralInteger) selector).setValue(inputInteger);
					selector.setName(ModelUtil.createUniqueName(lifeline.getNamespace(), "LiteralInteger_"));
					
				}  else if (elementClass.equals(LiteralUnlimitedNatural.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					Integer inputInteger;
					if (inputString.equals("*")) {
						inputInteger = -1;
					} else {
						inputInteger = Integer.parseInt(inputString);
					}
					
					// Create LiteralUnlimitedNatural
					selector = UMLFactory.eINSTANCE.createLiteralUnlimitedNatural();
					((LiteralUnlimitedNatural) selector).setValue(inputInteger);
					selector.setName(ModelUtil.createUniqueName(lifeline.getNamespace(), "LiteralUnlimitedNatural_"));
					
				}  else if (elementClass.equals(LiteralBoolean.class)) {
					String inputString = ((InputDialog) dialog).getValue();
					Boolean inputBoolean;
					if (inputString.equals("true")) {
						inputBoolean = true;
					} else {
						inputBoolean = false;
					}
					
					// Create LiteralBoolean
					selector = UMLFactory.eINSTANCE.createLiteralBoolean();
					((LiteralBoolean) selector).setValue(inputBoolean);
					selector.setName(ModelUtil.createUniqueName(lifeline.getNamespace(), "LiteralBoolean_"));
					
				}					 
				 
								
				if(selector != null) {
					lifeline.setSelector(selector);
				}
				
				refresh();
			}
		}
	}

	public void setContentProvider(ModelManager modelManager) {
		this.contentProvider = 
			new AdapterFactoryContentProvider(modelManager.getAdapterFactory());
	}
	
	public void setLabelProvider(ModelManager modelManager) {
		this.labelProvider = new UMLLabelProvider(modelManager.getLabelProvider());
	}

}
